package org.whh.automatic.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.whh.automatic.model.AutoTask;
import org.whh.automatic.model.Step;
import org.whh.automatic.util.MouseActionUtil;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.function.BiConsumer;

/**
 * 任务执行服务类
 * <p>
 * 该服务负责执行自动化任务，主要功能包括：
 * 1. 启动目标应用程序
 * 2. 按顺序执行任务中的每个步骤
 * 3. 支持三种操作类型：
 * - 快捷键操作：模拟键盘快捷键
 * - 坐标点击：模拟鼠标在特定位置点击
 * - 文本粘贴：支持固定文本和动态文本的粘贴
 * <p>
 * 使用Java Robot类实现底层的键盘和鼠标操作模拟。
 * 通过回调机制支持任务执行状态的实时反馈。
 */
@Service
public class TaskExecutorService {

    /**
     * 日志记录器
     */
    private static final Logger logger = LoggerFactory.getLogger(TaskExecutorService.class);

    /**
     * Robot实例
     * 用于模拟键盘和鼠标操作
     */
    private Robot robot;


    /**
     * 构造函数
     * <p>
     * 初始化Robot实例并设置默认的操作延迟，
     * 延迟的目的是使操作更稳定，避免执行过快导致目标程序无法及时响应
     */
    public TaskExecutorService() {
        try {
            robot = new Robot();
            // 设置默认延迟，使操作更稳定
            robot.setAutoDelay(50);
        } catch (AWTException e) {
            logger.error("创建Robot实例失败", e);
        }
    }

    /**
     * 执行自动任务（无回调版本）
     *
     * @param task 要执行的自动化任务
     */
    public void executeTask(AutoTask task) {
        executeTask(task, null);
    }

    /**
     * 执行自动任务（带回调版本）
     * <p>
     * 该方法会：
     * 1. 验证执行环境（Robot实例是否正常）
     * 2. 检查任务的动态值是否都已设置
     * 3. 启动目标应用程序
     * 4. 按顺序执行每个步骤
     * 5. 通过回调通知执行结果
     *
     * @param task     要执行的自动化任务
     * @param callback 执行结果回调函数
     */
    public void executeTask(AutoTask task, BiConsumer<Boolean, String> callback) {
        if (robot == null) {
            logger.error("Robot实例未初始化，无法执行任务");
            if (callback != null) {
                callback.accept(false, "Robot实例未初始化，无法执行任务");
            }
            return;
        }
        // 检查动态值是否都已设置
        if (!task.areAllDynamicValuesSet()) {
            logger.error("任务 {} 的动态值未完全设置，无法执行任务", task.getName());
            if (callback != null) {
                callback.accept(false, "动态值未完全设置，无法执行任务");
            }
            return;
        }
        long startTime = System.currentTimeMillis();
        logger.info("开始执行任务: {}", task.getName());
        try {
            String startFilePath = task.getStartFilePath();
            if (startFilePath != null && !startFilePath.trim().isEmpty()) {
                File file = new File(startFilePath);
                if (!file.exists() || !file.isFile()) {
                    logger.error("启动文件不存在: {}", startFilePath);
                    if (callback != null) {
                        callback.accept(false, "启动文件不存在，任务终止");
                    }
                    return;
                }
                // 启动应用程序
                startApplication(startFilePath);
                // 等待应用启动（使用配置的等待时间）
                int waitTime = task.getStartupWaitTime();
                if (waitTime > 0) {
                    logger.info("等待程序启动: {} 秒", waitTime);
                    sleep(waitTime * 1000);
                }
            }
            // 按顺序执行每个步骤
            for (Step step : task.getSteps()) {
                executeStep(step, task.getDynamicValues());
            }
            logger.info("任务执行完成: {}", task.getName());
            long endTime = System.currentTimeMillis();
            logger.info("任务执行耗时: {} 毫秒", endTime - startTime);
            if (callback != null) {
                callback.accept(true, "任务执行成功");
            }
        } catch (Exception e) {
            logger.error("任务执行失败: " + task.getName(), e);
            if (callback != null) {
                callback.accept(false, "任务执行失败: " + e.getMessage());
            }
        }
    }

    /**
     * 启动应用程序
     * <p>
     * 使用Windows系统命令启动目标应用程序
     *
     * @param filePath 应用程序文件路径
     * @throws IOException 如果启动过程出现IO错误
     */
    private void startApplication(String filePath) throws IOException {
        logger.info("启动应用程序: {}", filePath);

        // 使用系统命令启动应用
        ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "start", "", filePath);
        pb.start();
    }

    /**
     * 执行单个步骤
     * <p>
     * 根据步骤的类型执行相应的操作：
     * - SHORTCUT：执行快捷键操作
     * - COORDINATE：执行鼠标点击操作
     * - PASTE：执行文本粘贴操作
     * <p>
     * 每个步骤执行完成后会等待指定的时间，以确保操作的稳定性
     *
     * @param step          要执行的步骤
     * @param dynamicValues 动态值映射表
     */
    private void executeStep(Step step, Map<String, String> dynamicValues) {
        logger.info("执行步骤: {}", step.getName());

        try {
            switch (step.getActionType()) {
                case SHORTCUT:
                    // 使用快捷键
                    if (step.hasShortcut()) {
                        pressShortcut(step.getShortcut());
                    } else {
                        logger.warn("步骤 {} 没有定义有效的快捷键", step.getName());
                    }
                    break;

                case COORDINATE:
                    // 使用坐标点击
                    if (step.getCoordinate() != null) {
                        Point coordinate = step.getCoordinate();
                        boolean isDoubleClick = step.isDoubleClick();

                        logger.info("步骤 {} 将在坐标 ({}, {}) 执行{}操作",
                                step.getName(),
                                coordinate.x,
                                coordinate.y,
                                isDoubleClick ? "双击" : "单击");

                        // 直接使用MouseActionUtil执行点击操作
                        boolean success = MouseActionUtil.click(coordinate.x, coordinate.y, isDoubleClick);
                        if (!success) {
                            logger.warn("执行点击操作失败，坐标: ({}, {})", coordinate.x, coordinate.y);
                        }
                    } else {
                        logger.warn("步骤 {} 没有定义有效的坐标", step.getName());
                    }
                    break;

                case PASTE:
                    // 执行复制粘贴
                    executePaste(step, dynamicValues);
                    break;

                default:
                    logger.warn("步骤 {} 使用了未知的操作类型", step.getName());
                    break;
            }

            // 等待指定时间
            sleep(step.getWaitTime() * 1000);
        } catch (Exception e) {
            logger.error("执行步骤失败: " + step.getName(), e);
            throw new RuntimeException("执行步骤失败: " + step.getName(), e);
        }
    }

    /**
     * 执行复制粘贴操作
     * <p>
     * 支持两种粘贴模式：
     * 1. 固定值：使用预设的文本内容
     * 2. 动态值：从动态值映射表中获取文本内容
     * <p>
     * 实现过程：
     * 1. 确定要粘贴的文本内容
     * 2. 将文本复制到系统剪贴板
     * 3. 模拟Ctrl+V快捷键执行粘贴
     *
     * @param step          粘贴操作步骤
     * @param dynamicValues 动态值映射表
     */
    private void executePaste(Step step, Map<String, String> dynamicValues) {
        String textToPaste = null;

        if (step.getPasteValueType() == Step.PasteValueType.FIXED) {
            // 使用固定值
            textToPaste = step.getPasteFixedValue();
            logger.info("使用固定值粘贴: {}", textToPaste);
        } else if (step.getPasteValueType() == Step.PasteValueType.DYNAMIC) {
            // 使用动态值
            String dynamicName = step.getPasteDynamicName();
            textToPaste = dynamicValues.get(dynamicName);
            logger.info("使用动态值粘贴: {} = {}", dynamicName, textToPaste);
        }

        if (textToPaste == null || textToPaste.isEmpty()) {
            logger.warn("步骤 {} 没有可粘贴的内容", step.getName());
            return;
        }

        // 复制文本到剪贴板
        setClipboardText(textToPaste);

        // 执行粘贴快捷键 (Ctrl+V)
        robot.keyPress(KeyEvent.VK_CONTROL);
        robot.keyPress(KeyEvent.VK_V);
        robot.keyRelease(KeyEvent.VK_V);
        robot.keyRelease(KeyEvent.VK_CONTROL);
    }

    /**
     * 设置剪贴板文本
     *
     * @param text 要设置到剪贴板的文本内容
     */
    private void setClipboardText(String text) {
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        StringSelection selection = new StringSelection(text);
        clipboard.setContents(selection, null);
    }

    /**
     * 模拟按下快捷键
     * <p>
     * 支持常见的功能键（Ctrl、Alt、Shift、Win）和字母数字键的组合
     * 快捷键格式示例：
     * - "Ctrl+C"
     * - "Ctrl+Alt+Delete"
     * - "Win+R"
     * - "LEFT" 或 "←"（方向键）
     * - "HOME"、"END"、"PGUP"、"PGDN" 等特殊键
     *
     * @param shortcut 快捷键组合字符串，键名之间用+号分隔
     */
    private void pressShortcut(String shortcut) {
        logger.info("执行快捷键操作: {}", shortcut);
        String[] keys = shortcut.split("\\+");
        int[] keyCodes = new int[keys.length];

        // 转换快捷键为KeyEvent常量
        for (int i = 0; i < keys.length; i++) {
            String key = keys[i].trim().toUpperCase();
            try {
                switch (key) {
                    case "CTRL":
                        keyCodes[i] = KeyEvent.VK_CONTROL;
                        break;
                    case "ALT":
                        keyCodes[i] = KeyEvent.VK_ALT;
                        break;
                    case "SHIFT":
                        keyCodes[i] = KeyEvent.VK_SHIFT;
                        break;
                    case "WIN":
                        keyCodes[i] = KeyEvent.VK_WINDOWS;
                        break;
                    // 添加对方向键的专门处理
                    case "LEFT":
                    case "←":
                        keyCodes[i] = KeyEvent.VK_LEFT;
                        break;
                    case "RIGHT":
                    case "→":
                        keyCodes[i] = KeyEvent.VK_RIGHT;
                        break;
                    case "UP":
                    case "↑":
                        keyCodes[i] = KeyEvent.VK_UP;
                        break;
                    case "DOWN":
                    case "↓":
                        keyCodes[i] = KeyEvent.VK_DOWN;
                        break;
                    // 添加更多常用特殊键处理
                    case "HOME":
                        keyCodes[i] = KeyEvent.VK_HOME;
                        break;
                    case "END":
                        keyCodes[i] = KeyEvent.VK_END;
                        break;
                    case "PGUP":
                    case "PAGE_UP":
                        keyCodes[i] = KeyEvent.VK_PAGE_UP;
                        break;
                    case "PGDN":
                    case "PAGE_DOWN":
                        keyCodes[i] = KeyEvent.VK_PAGE_DOWN;
                        break;
                    case "INS":
                    case "INSERT":
                        keyCodes[i] = KeyEvent.VK_INSERT;
                        break;
                    case "DEL":
                    case "DELETE":
                        keyCodes[i] = KeyEvent.VK_DELETE;
                        break;
                    case "ESC":
                    case "ESCAPE":
                        keyCodes[i] = KeyEvent.VK_ESCAPE;
                        break;
                    case "TAB":
                        keyCodes[i] = KeyEvent.VK_TAB;
                        break;
                    case "SPACE":
                        keyCodes[i] = KeyEvent.VK_SPACE;
                        break;
                    case "ENTER":
                        keyCodes[i] = KeyEvent.VK_ENTER;
                        break;
                    default:
                        // 对于单个字符按键
                        if (key.length() == 1) {
                            keyCodes[i] = KeyEvent.getExtendedKeyCodeForChar(key.charAt(0));
                        } else {
                            // 尝试获取VK_常量
                            keyCodes[i] = KeyEvent.class.getField("VK_" + key).getInt(null);
                        }
                }
            } catch (Exception e) {
                logger.error("无法识别的按键: " + key, e);
                return;
            }
        }

        // 按下所有键
        for (int keyCode : keyCodes) {
            robot.keyPress(keyCode);
        }

        // 释放所有键（反向顺序）
        for (int i = keyCodes.length - 1; i >= 0; i--) {
            robot.keyRelease(keyCodes[i]);
        }
    }

    /**
     * 线程休眠
     * <p>
     * 用于在操作之间添加延迟，确保操作的稳定性
     * 如果休眠被中断，会重新设置线程的中断状态
     *
     * @param ms 休眠时间（毫秒）
     */
    private void sleep(int ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
